#include "Index.hpp"
#include "Util.hpp"
#include"Log.hpp"
#include <algorithm>
#include <jsoncpp/json/json.h>

namespace ns_searcher
{
    class Searcher
    {
        struct InvertedElemDedup//
        {
            uint64_t _doc_id;
            std::vector<std::string> _words;//序号为id的文档出现的所有关键词
            int _weight; // 关键词所有关键词的权重之和
            InvertedElemDedup()
            :_doc_id(0),_weight(0){}
        };

    public:
        Searcher()
        {
        }
        ~Searcher() {}
        void SearcherInit(const std::string &input)
        {
            // 创建Index单例+建立索引
            lg(_Info,"%s","建立索引中,请稍候....");
            //std::cout << "建立索引中,请稍候...." << std::endl;
            _index = ns_index::Index::GetInstance();
            _index->BuildIndex(input);
            lg(_Info,"%s","索引建立完毕");
            //std::cout << "索引建立完毕!" << std::endl;
        }
        // query是用户输入的搜索关键字
        // json_str是返回给用户浏览器的搜索结果
        void Search(const std::string &query, std::string *json_str)
        {
            // 1~对query进行分词
            std::vector<std::string> key_words;
            ns_util::JiebaUtil::CutString(query, &key_words);
            //std::vector<ns_index::InvertedElem> inverted_ele_all; // 保存所有的倒排元素
            std::unordered_map<uint64_t,InvertedElemDedup> tokens_map;//去重id后的结果

            for (auto &key_word : key_words)
            {
                // 查询的关键词全部转换为小写,提取出来的信息不区分大小写
                boost::to_lower(key_word);
                // 2~对分词结果 分别进行搜索
                ns_index::Index::InvertedList *inverted_list =
                    _index->GetInvertedList(key_word);
                if (inverted_list == nullptr)
                {
                    continue; // 这个词没能找到 对应的倒排拉链
                }
                //inverted_ele_all.insert(inverted_ele_all.end(), inverted_list->begin(), inverted_list->end());
                for(auto &elem: *inverted_list)
                {
                    auto& dedup_ele = tokens_map[elem._doc_id];
                    dedup_ele._doc_id = elem._doc_id;
                    dedup_ele._weight += elem._weight;
                    dedup_ele._words.push_back(elem._word);
                }
            }
            // 优化点:对所有的ele合并后指向的doc_id进行去重 这里只关心weight和id
            std::vector<InvertedElemDedup> inverted_ele_all;
            for(auto &kv:tokens_map)
            {
                inverted_ele_all.push_back(std::move(kv.second));
            }
            // 3~对所有的inverted_element按照wegiht排序
            // sort(inverted_ele_all.begin(), inverted_ele_all.end(),
            //      [](const ns_index::InvertedElem &left, const ns_index::InvertedElem &right)
            //      {
            //          return left._weight > right._weight;
            //      });
            sort(inverted_ele_all.begin(), inverted_ele_all.end(),[](InvertedElemDedup& left,InvertedElemDedup& right){
                return left._weight > right._weight;
            });
            // 4~序列化,构建json串返回给用户 -- 使用jsoncpp
            Json::Value ret;
            int cnt = 0; // debug
            for (auto &ele : inverted_ele_all)
            {
                ns_index::DocInfo *doc_info = _index->GetForwardIndex(ele._doc_id);
                if (doc_info == nullptr)
                    continue;
                Json::Value element;
                element["title"] = doc_info->_title;
                // 搜索时需要摘要,不是所有的content,后面优化
                element["desc"] = GetDesc(doc_info->_content, ele._words[0]);
                element["url"] = doc_info->_url;
                // debug:检查权重是否正确排序
                // element["weight"] = ele._weight;
                // element["word"] = ele._words[0];
                // element["id"] = (int)ele._doc_id; // json自动将int转化为string
                ret.append(element);
            }
            //Json::StyledWriter writer;
            Json::FastWriter writer;
            *json_str = writer.write(ret);
        }

    private:
        ns_index::Index *_index;

    private:
        std::string GetDesc(const std::string &html_content, const std::string &word)
        {
            // 找到word在content中首次出现的位置,向前截取prev_stepbyte,向后截取next_stepbyte
            // 向前<prev_step则从content开头开始,向后不足next_step则到content结尾
            // 1~ 找到word首次出现的位置
            std::cout << word << std::endl; // debug
            auto iter = std::search(html_content.begin(), html_content.end(), word.begin(), word.end(),
                                    [](int l, int r)
                                    {
                                        return std::tolower(l) == std::tolower(r);
                                    });
            if (iter == html_content.end())
            {
                lg(_Error,"%s","content里面没找到word");
                //std::cout << "content里面没找到word" << std::endl;
                return "None1";
            }
            // 找到了
            int pos = std::distance(iter, html_content.end());
            const int prev_step = 50;
            const int next_step = 50;
            // 2~ 确定begin和end位置

            int begin = pos >= prev_step ? pos - prev_step : 0;
            int end = (pos + next_step) < html_content.size() ? pos + next_step : html_content.size();

            // 3~ 截取描述子串[begin,end)并返回
            if (begin >= end) // end一定大于begin
            {
                lg(_Error,"%s","begin > end 越界了");
                //std::cout << "begin > end 越界了" << std::endl;
                return "None2";
            }
            std::string desc = html_content.substr(begin, end - begin);
            desc += "...";
            return desc;
        }

        // ----------------- 用find查找会出现None1问题
        // std::string GetDesc(const std::string &html_content,const std::string &word)
        // {
        //     //找到word在content中首次出现的位置,向前截取50byte,向后截取100byte
        //     //向前<50则从content开头开始,向后不足100则到content结尾
        //     //1~ 找到word首次出现的位置
        //     std::cout<<word<<std::endl;//debug
        //     size_t pos = html_content.find(word);
        //     if(pos == std::string::npos)
        //     {
        //         std::cout<<"content里面没找到word"<<std::endl;
        //         return "None1";
        //     }
        //     //找到了
        //     const size_t prev_step = 50;
        //     const size_t next_step = 100;
        //     //2~ 确定begin和end位置
        //     int begin = pos >= prev_step  ? pos-prev_step : 0;
        //     int end = (pos+next_step) < html_content.size() ? pos+next_step : html_content.size();

        //     //3~ 截取描述子串[begin,end)并返回
        //     if(begin>=end) //end一定大于begin
        //     {
        //         std::cout<<"begin > end 越界了"<<std::endl;
        //         return "None2";
        //     }
        //     std::string desc = html_content.substr(begin,end-begin);
        //     return desc;
        // }

        //     struct ComparedWeight
        //     {
        //         bool operator()(const ns_index::InvertedElem &left, const ns_index::InvertedElem &right) const
        //         {
        //             {
        //                 return left._weight > right._weight;
        //             }
        //         }
        //     };

        // private:
        //     bool compare_weight(const ns_index::InvertedElem &left, const ns_index::InvertedElem &right)
        //     {
        //         return left._weight > right._weight;
        //     }
    };
}